/*
 * Copyright (c) 2010. Dylan Schell.
 */

package nl.xs4all.dylanschell.deckprinter.pages;

import com.google.inject.Inject;
import nl.xs4all.dylanschell.deckprinter.model.CardInDeck;
import nl.xs4all.dylanschell.deckprinter.model.CardInSet;
import nl.xs4all.dylanschell.deckprinter.model.DeckDescriptor;
import nl.xs4all.dylanschell.deckprinter.model.SetDescriptor;
import nl.xs4all.dylanschell.deckprinter.services.DeckParser;
import nl.xs4all.dylanschell.deckprinter.services.DeckPrinter;
import nl.xs4all.dylanschell.deckprinter.services.ImageService;
import nl.xs4all.dylanschell.deckprinter.services.SetService;
import nl.xs4all.dylanschell.deckprinter.transform.DeckFilter;
import nl.xs4all.dylanschell.deckprinter.transform.ExplodeDeck;
import nl.xs4all.dylanschell.deckprinter.transform.FilterLands;
import nl.xs4all.dylanschell.deckprinter.transform.FilterNonLands;
import nl.xs4all.dylanschell.deckprinter.transform.FilterOther;
import nl.xs4all.dylanschell.deckprinter.transform.FilterSpell;
import nl.xs4all.dylanschell.deckprinter.transform.FilterType;
import nl.xs4all.dylanschell.deckprinter.transform.RemoveBasicLands;
import nl.xs4all.dylanschell.deckprinter.transform.RemoveUnknownCards;
import nl.xs4all.dylanschell.deckprinter.transform.ReplaceBasicLandsWithBasicLandsFromSet;
import org.apache.wicket.Component;
import org.apache.wicket.Resource;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.markup.html.DynamicWebResource;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.link.ResourceLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.util.time.Time;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: dylan
 * Date: Nov 22, 2010
 * Time: 9:03:49 PM
 * To change this template use File | Settings | File Templates.
 */
public class DeckViewPage extends BasePage
{
    @Inject
    public DeckPrinter printer;

    @Inject
    public DeckParser parser;

    @Inject
    public ImageService imageService;

    @Inject
    public SetService setService;

    private DeckDescriptor deck;

    private WebMarkupContainer deckView;
    private boolean removeBasicLands;
    private boolean useZendikarLands;

    public DeckViewPage(File f)
    {
        if (f != null)
        {
            try
            {
                FileInputStream fis = new FileInputStream(f);
                try
                {
                    deck = parser.parseDeck(fis, f.getName());
                    deck = new RemoveUnknownCards(setService, imageService).filter(deck);
                } finally
                {
                    fis.close();
                }
            } catch (IOException e)
            {
                error("Unable to load deck: " + e.getMessage());
            }
        }
        initGui();
    }

    public DeckViewPage(DeckDescriptor deck)
    {
        this.deck = deck;
        initGui();
    }

    private void initGui()
    {
        // create a feedback panel
        final Component feedback = new FeedbackPanel("feedback").setOutputMarkupPlaceholderTag(true);
        add(feedback);

        final WebMarkupContainer options = new WebMarkupContainer("options")
        {
            @Override
            protected void onConfigure()
            {
                setVisible(deck != null);
            }
        };
        options.setOutputMarkupId(true);
        options.setOutputMarkupPlaceholderTag(true);
        add(options);
        Form<?> settingsForm = new Form<Void>("settingsForm");
        options.add(settingsForm);

        CheckBox removeBasicLands = new CheckBox("removeBasicLands", new PropertyModel<Boolean>(this, "removeBasicLands"));
        removeBasicLands.add(new AjaxFormComponentUpdatingBehavior("onchange")
        {
            @Override
            protected void onUpdate(AjaxRequestTarget ajaxRequestTarget)
            {
                ajaxRequestTarget.addComponent(deckView);
            }
        });
        settingsForm.add(removeBasicLands);

        CheckBox useZendikar = new CheckBox("replaceWithZendikar", new PropertyModel<Boolean>(this, "useZendikarLands"));
        useZendikar.add(new AjaxFormComponentUpdatingBehavior("onchange")
        {
            @Override
            protected void onUpdate(AjaxRequestTarget ajaxRequestTarget)
            {
                ajaxRequestTarget.addComponent(deckView);
            }
        });
        settingsForm.add(useZendikar);

        final WebMarkupContainer actions = new WebMarkupContainer("actions");
        actions.setOutputMarkupId(true);
        actions.setOutputMarkupPlaceholderTag(true);
        add(actions);

        Link home = new BookmarkablePageLink("home", getApplication().getHomePage());
        actions.add(home);

        Link edit = new Link("edit")
        {
            public void onClick()
            {
                setResponsePage(new DeckEditPage(deck));
            }
        };
        actions.add(edit);

        Resource deckResource = new DynamicWebResource("deck.pdf")
        {
            @Override
            protected ResourceState getResourceState()
            {
                return new ResourceState()
                {
                    @Override
                    public Time lastModifiedTime()
                    {
                        return Time.now();
                    }

                    @Override
                    public byte[] getData()
                    {
                        DeckDescriptor deck = getTransformedDeck();
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        printer.printDeck(deck, bos);
                        return bos.toByteArray();
                    }

                    @Override
                    public String getContentType()
                    {
                        return "application/pdf";
                    }
                };
            }
        };

        Link upload = new BookmarkablePageLink("upload", UploadDeckPage.class);
        actions.add(upload);

        Link download = new ResourceLink("download", deckResource)
        {
            @Override
            protected void onConfigure()
            {
                super.onConfigure();
                setVisible(deck != null);
            }
        };
        download.setOutputMarkupId(true);
        actions.add(download);

        Link preview = new Link("preview")
        {
            @Override
            protected void onConfigure()
            {
                super.onConfigure();
                setVisible(deck != null);
            }

            @Override
            public void onClick()
            {
                DeckDescriptor desc = new ExplodeDeck().filter(getTransformedDeck());
                setResponsePage(new DeckPreviewPage(desc, getPage()));
            }
        };
        actions.add(preview);

        deckView = new WebMarkupContainer("deckview")
        {
            @Override
            protected void onConfigure()
            {
                super.onConfigure();
                setVisible(deck != null);
            }
        };
        deckView.setOutputMarkupId(true);
        deckView.setOutputMarkupPlaceholderTag(true);
        add(deckView);

        PropertyModel<DeckDescriptor> deckModel = new PropertyModel<DeckDescriptor>(this, "transformedDeck");
        deckView.add(new DeckList(
                "lands",
                new DeckSectionModel(
                        DeckDescriptor.SECTION_MAIN,
                        new FilteredDeckModel(
                                deckModel,
                                new FilterLands()))));
        deckView.add(new DeckList(
                "creatures",
                new DeckSectionModel(
                        DeckDescriptor.SECTION_MAIN,
                        new FilteredDeckModel(
                                new PropertyModel(this, "transformedDeck"),
                                new FilterType("Creature")))));
        deckView.add(new DeckList(
                "artifacts",
                new DeckSectionModel(
                        DeckDescriptor.SECTION_MAIN,
                        new FilteredDeckModel(
                                deckModel,
                                new FilterType("Artifact")))));
        deckView.add(new DeckList(
                "spells",
                new DeckSectionModel(
                        DeckDescriptor.SECTION_MAIN,
                        new FilteredDeckModel(
                                deckModel,
                                new FilterSpell()))));
        deckView.add(new DeckList(
                "other",
                new DeckSectionModel(
                        DeckDescriptor.SECTION_MAIN,
                        new FilteredDeckModel(
                                deckModel,
                                new FilterOther()))));
        deckView.add(new DeckList("sideboard", new DeckSectionModel(DeckDescriptor.SECTION_SIDEBOARD, deckModel)));

        Chart chart = new Chart("manacurve");
        int[] manaCurve = calculateManaCurve(getTransformedDeck());
        chart.setSeries(manaCurve);
        add(chart);
    }

    @Override
    public String getTitle()
    {
        return "Deck Printer";
    }

    public boolean isRemoveBasicLands()
    {
        return removeBasicLands;
    }

    public void setRemoveBasicLands(boolean removeBasicLands)
    {
        this.removeBasicLands = removeBasicLands;
    }

    public boolean isUseZendikarLands()
    {
        return useZendikarLands;
    }

    public void setUseZendikarLands(boolean useZendikarLands)
    {
        this.useZendikarLands = useZendikarLands;
    }

    public DeckDescriptor getTransformedDeck()
    {
        DeckDescriptor result = deck;
        if (result != null)
        {
            if (removeBasicLands)
            {
                result = new RemoveBasicLands().filter(result);
            }
            if (useZendikarLands)
            {
                for (SetDescriptor sd : setService.getAllSets())
                {
                    if (sd.getName().equals("Zendikar"))
                    {
                        result = new ReplaceBasicLandsWithBasicLandsFromSet(sd).filter(result);
                    }
                }
            }
        }
        return result;
    }

    private class FilteredDeckModel extends LoadableDetachableModel<DeckDescriptor>
    {
        private DeckFilter[] filters;
        private IModel<DeckDescriptor> model;

        public FilteredDeckModel(IModel<DeckDescriptor> model, DeckFilter... filter)
        {
            this.filters = filter;
            this.model = model;
        }

        @Override
        protected DeckDescriptor load()
        {
            DeckDescriptor deck = model.getObject();
            if (filters != null)
            {
                for (DeckFilter filter : filters)
                {
                    deck = filter.filter(deck);
                }
            }
            return deck;
        }

        @Override
        public void detach()
        {
            super.detach();
            model.detach();
        }
    }

    private class DeckSectionModel extends AbstractReadOnlyModel<List<CardInDeck>>
    {
        private final String section;
        private IModel<DeckDescriptor> deckModel;

        public DeckSectionModel(String section, IModel<DeckDescriptor> deckModel)
        {
            this.section = section;
            this.deckModel = deckModel;
        }

        @Override
        public List<CardInDeck> getObject()
        {
            return deckModel.getObject().getCardsInSection(section);
        }

        @Override
        public void detach()
        {
            deckModel.detach();
        }
    }

    private class DeckList extends ListView<CardInDeck>
    {
        public DeckList(String id, IModel<List<CardInDeck>> model)
        {
            super(id, model);
        }

        @Override
        protected void populateItem(ListItem<CardInDeck> cardInDeckListItem)
        {
            cardInDeckListItem.add(new Label("amount", new PropertyModel(cardInDeckListItem.getModelObject(), "quantity")));
            cardInDeckListItem.add(new Label("cardName", new PropertyModel(cardInDeckListItem.getModelObject(), "name")));
        }
    }

    private int[] calculateManaCurve(DeckDescriptor deck)
    {
        deck = new FilterNonLands().filter(deck);
        int[] result = new int[20];
        for (CardInDeck cid : deck.getMain())
        {
            int q = cid.getQuantity();
            CardInSet cis = setService.getCardInSet(cid);
            int cmc = 0;
            if (cis != null)
            {
                try
                {
                    cmc = Integer.parseInt(cis.getConvertedManaCost());
                    result[cmc] += q;
                } catch (NumberFormatException nfe)
                {

                }
            }
        }
        int max = 20;
        for (int i = 19; i >= 0; i--)
        {
            if (result[i] != 0)
            {
                break;
            }
            max = i;
        }
        int[] target = new int[max];
        System.arraycopy(result, 0, target, 0, max);
        return target;
    }
}

